#include "MoveableObject.h"
#include "GameApp.h"

//------------------------------------------------------------------------------------
MoveableObject::MoveableObject(std::string objectID)
	: BaseObject(objectID), mfMaxSpeed(0), mfJumpPower(0), mMoveSound(0)
{
	mfSpeed = 0.0f;
	mMoveState = MOVE_IDLE;
	mCurrentAttack = MOVE_IDLE;
	mOrientation = 0;
	savedFriction = 0.f;
	mForceAir = 0.f;

	boost::shared_ptr<SettingsManager> sm(new SettingsManager(objectID));
	readMoveableFromXML(sm);
	mTypes.push_front(OBJECT_MOVEABLE);
}
//------------------------------------------------------------------------------------
MoveableObject::MoveableObject(boost::shared_ptr<SettingsManager> sm)
	: BaseObject(sm), mfMaxSpeed(0), mfJumpPower(0), mMoveSound(0)
{
	mfSpeed = 0.0f;
	mMoveState = MOVE_IDLE;
	mCurrentAttack = MOVE_IDLE;
	mOrientation = 0;
	savedFriction = 0.f;
	mForceAir = 0.f;

	readMoveableFromXML(sm);
	mTypes.push_front(OBJECT_MOVEABLE);
}
//------------------------------------------------------------------------------------
MoveableObject::MoveableObject(const OgreDataPtr ogreData, const BulletDataPtr bulletData,
							   const float maxSpeed, const float jumpPower)
	: BaseObject(ogreData, bulletData), mfMaxSpeed(maxSpeed), mfJumpPower(jumpPower), mMoveSound(0)
{
	mfSpeed = 0.0f;
	mMoveState = MOVE_IDLE;
	mCurrentAttack = MOVE_IDLE;
	mOrientation = 0;
	savedFriction = 0.f;
	mForceAir = 0.f;
	mTypes.push_front(OBJECT_MOVEABLE);
}
//------------------------------------------------------------------------------------
MoveableObject::~MoveableObject(void)
{
}
//------------------------------------------------------------------------------------
void MoveableObject::update(const unsigned long elapsedTime)
{
	mpAnimator->stepAnimation(elapsedTime);
	updateAirStatus(elapsedTime);
/*
	switch (mMoveState)
	{
	case MOVE_IDLE:
		break;
	case MOVE_NORMAL:
		if (inAir)
		{
			mpAnimator->playAnimation(Ogre::String("Jump_down"));
			mMoveState = MOVE_JUMPDOWN;
			break;
		}

		if (!moving)
		{
			gpApp->mpAudio->stopSound(mMoveSound);
			mpAnimator->playAnimation(Ogre::String("Idle"), true);
			mMoveState = MOVE_IDLE;
		}
		moving = false;
		break;
	case MOVE_JUMPUP:
		if (getLocalVelocity().y() < 0.f)
		{
			mpAnimator->playAnimation(Ogre::String("Jump_down"));
			mMoveState = MOVE_JUMPDOWN;
		}
		break;
	case MOVE_JUMPDOWN:
		break;
	case MOVE_NORMALATTACK:
		break;
	default:
		return;
	}
*/
	
	if(mStates[MOVE_ATTACK])
	{
		Ogre::AnimationState *anim = mpAnimator->getAnimation(Ogre::String("Attack"));
		if(anim->getTimePosition() == anim->getLength())
		{
			mStates[MOVE_ATTACK] = false;
		}else return;
	}

	//play correct animation to the movement state
	if(mStates[MOVE_AIR])
	{
		if (getLocalVelocity().y() < 0.f)
		{
			mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Jump_down"));
		} else
		{
			mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Jump_up"));
		}
	}
	else if (!mStates[MOVE_NORMAL])
	{
		gpApp->mpAudio->stopSound(mMoveSound);
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Idle"), true);
	}
	mStates[MOVE_NORMAL] = false;
}
//------------------------------------------------------------------------------------
void MoveableObject::readMoveableFromXML(boost::shared_ptr<SettingsManager> sm)
{
	mfMaxSpeed = (float)atof(sm->getSetting("MoveData/speed").c_str());
	mfJumpPower = (float)atof(sm->getSetting("MoveData/jump").c_str());
}
//------------------------------------------------------------------------------------
const float MoveableObject::getSpeed(void) const
{
	return mfSpeed;
}
//------------------------------------------------------------------------------------
void MoveableObject::setSpeed(const float speed)
{
	mfSpeed = speed;
}
//------------------------------------------------------------------------------------
const float MoveableObject::getMaxSpeed(void) const
{
	return mfMaxSpeed;
}
//------------------------------------------------------------------------------------
const float MoveableObject::getJumpPower(void) const
{
	return mfJumpPower;
}
//------------------------------------------------------------------------------------
void MoveableObject::setSound(const SoundType type, const ALuint source)
{
	if (type == SOUND_IDLE) mIdleSound = source;
	if (type == SOUND_MOVE) mMoveSound = source;
}
//------------------------------------------------------------------------------------
const ALuint MoveableObject::getSound(const SoundType type) const
{
	if (type == SOUND_IDLE) return mIdleSound;
	if (type == SOUND_MOVE) return mMoveSound;

	return 0;
}
//------------------------------------------------------------------------------------
void MoveableObject::moveLeft(void)
{
	if(mStates[MOVE_ATTACK] || !(mMoveState == MOVE_IDLE)) return;

	if (!mStates[MOVE_LEFT])
	{
		lookLeft();
	}
	
		float maxSpeed;
		if(mStates[MOVE_AIR]) maxSpeed = mfMaxSpeed * 0.5f;
		else maxSpeed = mfMaxSpeed;
		btVector3 vel = getLocalVelocity();
		if(vel.x() > -maxSpeed) vel.setX(-maxSpeed);
		setLocalVelocity(vel);
	
	mpBody->activate();
	
	if(!mStates[MOVE_AIR]) 
	{
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Run"),true);
		//mpAnimator->playAnimation(ANIM_LOWER, Ogre::String("Run_Lower"),true);
		gpApp->mpAudio->playSound(getSound(SOUND_MOVE));
	}

	mStates[MOVE_NORMAL] = true;
}
//------------------------------------------------------------------------------------
void MoveableObject::moveRight(void)
{
	if(mStates[MOVE_ATTACK] || !(mMoveState == MOVE_IDLE)) return;

	if (mStates[MOVE_LEFT])
	{
		lookRight();
	}
	
	float maxSpeed;
	if(mStates[MOVE_AIR]) maxSpeed = mfMaxSpeed * 0.5f;
	else maxSpeed = mfMaxSpeed;
	btVector3 vel = getLocalVelocity();
	if(vel.x() < maxSpeed) vel.setX(maxSpeed);
	setLocalVelocity(vel);

	mpBody->activate();
	
	if(!mStates[MOVE_AIR]) 
	{
		mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Run"),true);
		//mpAnimator->playAnimation(ANIM_LOWER, Ogre::String("Run_Lower"),true);
		gpApp->mpAudio->playSound(getSound(SOUND_MOVE));
	}

	mStates[MOVE_NORMAL] = true;
}
//------------------------------------------------------------------------------------
void MoveableObject::jump(void)
{
	if(mStates[MOVE_ATTACK] || !(mMoveState == MOVE_IDLE)) return;

	if(mStates[MOVE_AIR]) return;

	btVector3 vel = getLocalVelocity();
	if(vel.y() < mfJumpPower) vel.setY(mfJumpPower);
	setLocalVelocity(vel);

	gpApp->mpAudio->stopSound(getSound(SOUND_MOVE));
	gpApp->mpAudio->playSound(getSound(SOUND_JUMP));

	mpAnimator->playAnimation(ANIM_FULL, Ogre::String("Jump_up"));
	//mpAnimator->playAnimation(ANIM_LOWER, Ogre::String("Jump_Up_Lower"));

	mpBody->activate();
	mForceAir = 0.2f;
	//remove friction so you dont get stuck on walls
	if(mpBody->getFriction() != 0.f)
	{
		savedFriction = mpBody->getFriction();
		mpBody->setFriction(0.f);
	}
}
//-------------------------------------------------------------------------------------
void MoveableObject::updateAirStatus(const unsigned long elapsedTime)
{
	//checking if force air status is still active
	mForceAir -= elapsedTime / 1000.f;
	if(mForceAir <= 0.f)
	{
		mForceAir = 0.f;

		btVector3 start, end, normal;
		short mask = COL_LEVEL;
		GamePhysics *p = gpApp->mpGameLogic->getPhysics();
		start = btVector3(mpBody->getWorldTransform().getOrigin());
		end = btVector3(mpBody->getGravity());
		end.normalize();
		end = start + end * (physicsSize().y() * 0.5f);

		mStates[MOVE_AIR] = !p->raycast(start, end, normal, mask);
	} else
		mStates[MOVE_AIR] = true;
	//changing friction, so you slide down walls
	if(mStates[MOVE_AIR])
	{
		if(mpBody->getFriction() != 0.f)
		{
			savedFriction = mpBody->getFriction();
			mpBody->setFriction(0.f);
		}
	}else
	{
		if(mpBody->getFriction() == 0.f)
		{
			mpBody->setFriction(savedFriction);
		}
	}
}
//-------------------------------------------------------------------------------------
void MoveableObject::updateWallStatus(const unsigned long elapsedTime)
{
	btVector3 start, end, normal;
	short mask = COL_LEVEL;
	GamePhysics *p = gpApp->mpGameLogic->getPhysics();
	//check right wall
	start = btVector3(mpBody->getWorldTransform().getOrigin());
	end = getGlobalVector(btVector3(1.f,0.f,0.f) * (physicsSize().x() * 0.5f + 0.2f));

	mStates[MOVE_WALL_RIGHT] = p->raycast(start, end, normal, mask);

	//check left wall
	start = btVector3(mpBody->getWorldTransform().getOrigin());
	end = getGlobalVector(btVector3(-1.f,0.f,0.f) * (physicsSize().x() * 0.5f + 0.2f));

	mStates[MOVE_WALL_LEFT] = p->raycast(start, end, normal, mask);
}
//-------------------------------------------------------------------------------------
void MoveableObject::lookLeft(void)
{
	Ogre::Matrix3 mat;
	getEntityNode()->getOrientation().ToRotationMatrix(mat);
	mat[0][0] = 0.0f;
	mat[1][0] = 0.0f;
	mat[2][0] = 1.0f;
		
	mat[0][2] = -1.0f;
	mat[1][2] = 0.0f;
	mat[2][2] = 0.0f;
	Ogre::Quaternion quat;
	quat.FromRotationMatrix(mat);
	getEntityNode()->setOrientation(quat);
	mStates[MOVE_LEFT] = true;
}
//-------------------------------------------------------------------------------------
void MoveableObject::lookRight(void)
{
	Ogre::Matrix3 mat;
	getEntityNode()->getOrientation().ToRotationMatrix(mat);
	mat[0][0] = 0.0f;
	mat[1][0] = 0.0f;
	mat[2][0] = -1.0f;
		
	mat[0][2] = 1.0f;
	mat[1][2] = 0.0f;
	mat[2][2] = 0.0f;
	Ogre::Quaternion quat;
	quat.FromRotationMatrix(mat);
	getEntityNode()->setOrientation(quat);
	mStates[MOVE_LEFT] = false;
}
//-------------------------------------------------------------------------------------
const Ogre::Vector3 MoveableObject::getMouseDirection(void) const
{
	btVector3 pos, right;
	gpApp->mpGameLogic->getPhysics()->getDirectionVecs(pos, right);
	Ogre::Camera *cam = gpApp->mpView->getCamera()->getCamera();

	Ogre::Ray ogreRay = cam->getCameraToViewportRay(0.5f, 0.5f);
	return ogreRay.getOrigin() - cvt(pos);
}
//-------------------------------------------------------------------------------------
const float MoveableObject::getMouseXDirection(void) const
{
	btVector3 pos, right;
	gpApp->mpGameLogic->getPhysics()->getDirectionVecs(pos, right);

	return (mpBody->getWorldTransform().getOrigin() - pos).dot(right);
}
//-------------------------------------------------------------------------------------
const float MoveableObject::getMouseAngle(void) const
{
	btVector3 pos, right;
	gpApp->mpGameLogic->getPhysics()->getDirectionVecs(pos, right);
	Ogre::Camera *cam = gpApp->mpView->getCamera()->getCamera();

	Ogre::Ray ogreRay = cam->getCameraToViewportRay(0.5f, 0.5f);
	return ((ogreRay.getOrigin() - cvt(pos)).angleBetween(cam->getOrientation().yAxis())).valueRadians();
}
//-------------------------------------------------------------------------------------
const Ogre::Quaternion MoveableObject::getMouseRotation(void) const
{
	btVector3 pos, right;
	gpApp->mpGameLogic->getPhysics()->getDirectionVecs(pos, right);
	Ogre::Camera *cam = gpApp->mpView->getCamera()->getCamera();
	
	Ogre::Ray ogreRay = cam->getCameraToViewportRay(0.5f, 0.5f);
	return cvt(right).getRotationTo(ogreRay.getOrigin() - cvt(pos));
}
//-------------------------------------------------------------------------------------
map<MoveState, bool> &MoveableObject::getStates(void)
{
	return mStates;
}
//-------------------------------------------------------------------------------------
const MoveState MoveableObject::getMoveState(void) const
{
	return mMoveState;
}
//-------------------------------------------------------------------------------------
void MoveableObject::scalePower(const char power)
{
}